function fill_offsets_to_csv(input_path, output_path)
% fill_offsets_to_csv 读取表格、为每个 FORMAT_NAME 分组填写 offset，并导出 CSV
%
% 用法：
%   fill_offsets_to_csv('QQQQQ.xlsx', 'QQQQQ_with_offset.csv');
%
% 规则：
%   1) 以列 FORMAT_NAME 分组，每组的 offset 从 0 开始。
%   2) 若存在"长度列"（优先顺序：LENGTH、LEN、SIZE、BYTES、BIT_LENGTH/宽度换算），
%      则 offset 为该组中长度的前缀和（cumsum 的移位），单位按该列含义计算。
%      - 如果只有 bit 长度（BIT_LENGTH/BITS），默认按字节对齐：ceil(bits/8)；
%   3) 若找不到任何长度列，则按行序号从 0,1,2,... 依次递增。
%   4) 若原表本就含 OFFSET 列，将被覆盖重算。
%
% 说明：
%   - 列名匹配不区分大小写，允许下划线或空格变体（如 Format_Name、format name）。
%   - 支持 Excel（.xlsx/.xls/.xlsm）和 CSV/TSV。
%
% 作者：你最靠谱的同事（就是我 :））

    %----------------------------
    % 读取表格
    %----------------------------
    opts = detectImportOptions(input_path, 'NumHeaderLines', 0);
    opts = setvaropts(opts, opts.VariableNames, 'WhitespaceRule', 'preserve');
    opts = setvaropts(opts, opts.VariableNames, 'EmptyFieldRule', 'auto');
    tbl  = readtable(input_path, opts);

    %----------------------------
    % 列名规范化与定位
    %----------------------------
    var_map = lower(strrep(strrep(string(tbl.Properties.VariableNames), ' ', ''), '_', ''));
    get_col_idx = @(cands) find(ismember(var_map, lower(strrep(strrep(string(cands), ' ', ''), '_', ''))), 1, 'first');

    % 必需列：FORMAT_NAME
    format_name_idx = get_col_idx(["FORMAT_NAME","FORMATNAME","formatname","format_name","格式名","类型名"]);
    if isempty(format_name_idx)
        error('找不到必需列 FORMAT_NAME（或其等价命名）。');
    end
    format_name_col = tbl.Properties.VariableNames{format_name_idx};

    % 可选长度列（优先级从上到下）
    length_candidates = [ ...
        "LENGTH","LEN","SIZE","BYTES","BYTE_LENGTH","FIELD_LENGTH", ...
        "BIT_LENGTH","BITS","BITLEN","WIDTH_BITS","BITWIDTH" ...
    ];
    length_idx = get_col_idx(length_candidates);
    has_length = ~isempty(length_idx);

    % 找/建 OFFSET 列
    offset_idx = get_col_idx(["OFFSET","offset","偏移","起始偏移"]);
    if isempty(offset_idx)
        % 新增列并放到最后
        tbl.OFFSET = zeros(height(tbl), 1, 'double');
        offset_name = "OFFSET";
    else
        offset_name = tbl.Properties.VariableNames{offset_idx};
        % 先清零；稍后重算
        tbl.(offset_name) = zeros(height(tbl), 1, 'double');
    end

    %----------------------------
    % 预处理长度列（若存在）
    %----------------------------
    if has_length
        length_name = tbl.Properties.VariableNames{length_idx};
        len_raw = tbl.(length_name);

        % 尝试将文本数字化
        if iscell(len_raw) || isstring(len_raw) || ischar(len_raw) || iscategorical(len_raw)
            len_num = str2double(string(len_raw));
        else
            len_num = double(len_raw);
        end

        % 处理 NaN
        len_num(~isfinite(len_num)) = 0;

        % 如果识别为 bit 长度（列名包含 "BIT"）
        is_bits = contains(lower(length_name), "bit");
        if is_bits
            % 缺省按字节对齐
            byte_len = ceil(len_num/8);
            len_bytes = byte_len;
        else
            % 认为单位就是字节
            len_bytes = len_num;
        end
        length_bytes = max(len_bytes, 0);
    else
        length_bytes = [];
    end

    %----------------------------
    % 按 FORMAT_NAME 分组计算 offset
    %----------------------------
    [grp_ids, grp_keys] = findgroups(tbl.(format_name_col));

    % 组内顺序使用原始行序
    n = height(tbl);
    offsets = zeros(n, 1, 'double');

    for g = 1:max(grp_ids)
        rows = find(grp_ids == g);

        if isempty(rows)
            continue;
        end

        if has_length
            % 组内 offset = [0; cumsum(length)] 的去尾移位
            lbytes = length_bytes(rows);
            % 将负值/空值处理为 0
            lbytes(~isfinite(lbytes)) = 0;
            lbytes(lbytes < 0) = 0;

            csum = cumsum(lbytes, 'omitnan');
            offset_group = [0; csum(1:end-1)];
        else
            % 无长度列：按序号 0,1,2,... 填充
            offset_group = (0:numel(rows)-1).';
        end

        offsets(rows) = offset_group;
    end

    tbl.(offset_name) = offsets;

    %----------------------------
    % 导出 CSV（UTF-8）
    %----------------------------
    % 为避免科学计数法或小数点问题，可设置写出选项
    writetable(tbl, output_path, 'FileType', 'text', 'QuoteStrings', true);

    % 输出提示
    fprintf('已写出：%s\n', output_path);
end
